import os
import openpyxl
from configparser import ConfigParser
from src.logger import logger
from typing import List, Dict, Optional


class ExcelHandler:
    def __init__(self, dir_path=None):
        # 读取配置
        self.config = ConfigParser()
        self.config.read(os.path.join(
            os.path.dirname(os.path.dirname(__file__)),
            'config', 'config.ini'
        ), encoding='utf-8')

        # 若未指定目录，使用配置中的默认数据目录（默认为data文件夹）
        if not dir_path:
            self.dir_path = os.path.join(
                os.path.dirname(os.path.dirname(__file__)),
                'data'
            )
        else:
            self.dir_path = dir_path

        # 从配置读取工作表名（默认Sheet1）
        self.sheet_name = self.config.get('EXCEL', 'sheet_name', fallback='Sheet1')

        # 存储已打开的工作簿和工作表（键为文件路径）
        self.workbooks = {}  # {file_path: workbook_obj}
        self.sheets = {}  # {file_path: sheet_obj}

        # 缓存用例数据（避免重复读取）
        self.case_cache = {}  # {file_path: cases_list}

    def get_all_excel_files(self) -> List[str]:
        """获取目录下所有Excel文件（.xlsx/.xls）"""
        excel_files = []
        for filename in os.listdir(self.dir_path):
            # 过滤Excel文件（排除临时文件~$xxx.xlsx）
            if (filename.endswith(('.xlsx', '.xls')) and
                    not filename.startswith('~$')):
                file_path = os.path.abspath(os.path.join(self.dir_path, filename))
                excel_files.append(file_path)

        logger.info(f"在目录 {self.dir_path} 中找到 {len(excel_files)} 个Excel文件")
        return excel_files

    def open(self, file_path: str):
        """打开单个Excel文件并缓存工作表"""
        if file_path in self.workbooks:
            return  # 已打开则跳过

        try:
            # 加载工作簿（read_only=False支持写入）
            workbook = openpyxl.load_workbook(file_path, read_only=False, data_only=False)

            # 选择工作表（优先配置的sheet_name，不存在则用第一个sheet）workbook.sheetnames 是名称列表
            if self.sheet_name in workbook.sheetnames:
                sheet = workbook[self.sheet_name]
            else:
                sheet = workbook[workbook.sheetnames[0]]
                logger.warning(f"工作表 {self.sheet_name} 不存在，使用第一个工作表: {sheet.title}")

            self.workbooks[file_path] = workbook
            self.sheets[file_path] = sheet
            logger.info(f"打开Excel文件: {file_path}（工作表: {sheet.title}）")

        except Exception as e:
            logger.error(f"打开文件 {file_path} 失败: {str(e)}")
            raise

    def get_headers(self, file_path: str) -> List[str]:
        """获取单个Excel文件的表头（第一行）"""
        self.open(file_path)
        sheet = self.sheets[file_path]

        headers = []
        for cell in sheet[1]:  # 假设第一行为表头
            headers.append(cell.value if cell.value is not None else f"col_{cell.column}")
        return headers

    def get_cases(self, module: Optional[str] = None, priority: Optional[str] = None) -> List[Dict]:
        """
        读取所有Excel文件的用例，支持按模块和优先级筛选
        :param module: 模块名称（如"用户模块"，None表示全部）
        :param priority: 用例等级（如"P0"，None表示全部）
        :return: 筛选后的用例列表（每个用例含"_file_path"和"_row_num"标识来源）
        """
        all_cases = []
        excel_files = self.get_all_excel_files()

        for file_path in excel_files:
            # 读取并缓存该文件的所有用例
            if file_path not in self.case_cache:
                headers = self.get_headers(file_path)
                sheet = self.sheets[file_path]
                file_cases = []

                # 从第二行开始读取数据行
                for row_num in range(2, sheet.max_row + 1):
                    case = {
                        "_file_path": file_path,  # 记录用例来源文件
                        "_row_num": row_num  # 记录用例行号（用于写入结果）
                    }

                    # 读取当前行的所有列
                    for col_num in range(1, len(headers) + 1):
                        header = headers[col_num - 1]
                        cell_value = sheet.cell(row=row_num, column=col_num).value
                        case[header] = cell_value

                    # 只处理需要执行的用例（is_run为yes）
                    if case.get("is_run", "").lower() == "yes":
                        file_cases.append(case)

                self.case_cache[file_path] = file_cases
                logger.info(f"从 {os.path.basename(file_path)} 读取 {len(file_cases)} 条用例")

            # 按模块和优先级筛选当前文件的用例
            filtered = []
            for case in self.case_cache[file_path]:
                # 模块筛选（忽略大小写）
                if module and str(case.get("module", "")).lower() != module.lower():
                    continue
                # 优先级筛选（忽略大小写）
                if priority and str(case.get("priority", "")).lower() != priority.lower():
                    continue
                filtered.append(case)

            all_cases.extend(filtered)

        logger.info(f"筛选后共获取 {len(all_cases)} 条用例（模块: {module or '全部'}, 等级: {priority or '全部'}）")
        return all_cases

    def write_result(self, case: Dict, column: int, value: str):
        """
        写入测试结果到用例所在的Excel文件
        :param case: 用例字典（需包含"_file_path"和"_row_num"键）
        :param column: 结果列索引（从1开始）
        :param value: 结果值（如"Passed"/"Failed"）
        """
        file_path = case.get("_file_path")
        row_num = case.get("_row_num")

        if not file_path or not row_num:
            logger.error("用例缺少来源文件或行号信息，无法写入结果")
            return

        self.open(file_path)

        try:
            sheet = self.sheets[file_path]
            sheet.cell(row=row_num, column=column).value = value
            self.workbooks[file_path].save(file_path)  # 保存文件
            logger.info(f"写入结果到 {os.path.basename(file_path)} 第{row_num}行: {value}")
        except Exception as e:
            logger.error(f"写入结果失败: {str(e)}")
            raise

    def close(self):
        """关闭所有打开的Excel文件，释放资源"""
        for file_path, workbook in self.workbooks.items():
            workbook.close()
            logger.info(f"关闭Excel文件: {file_path}")
        self.workbooks.clear()
        self.sheets.clear()

    def __del__(self):
        """对象销毁时自动关闭文件"""
        self.close()
